package com.android.nearshop.business.location;

import java.io.IOException;

import org.apache.http.client.ClientProtocolException;
import org.json.JSONException;

import android.content.Context;
import android.location.Location;
import android.os.Handler;
import android.util.Log;

import com.android.nearshop.app.MyApplication;
import com.android.nearshop.business.cache.DataCacheManager;
import com.android.nearshop.business.model.Addr;
import com.android.nearshop.util.Constant;
import com.android.nearshop.util.CycledThread;
import com.android.nearshop.util.CycledThread.OnTimeoutListener;
import com.android.nearshop.util.Util;
import com.baidu.mapapi.BMapManager;
import com.baidu.mapapi.LocationListener;
import com.baidu.mapapi.MKAddrInfo;
import com.baidu.mapapi.MKGeneralListener;
import com.baidu.mapapi.MKGeocoderAddressComponent;
import com.baidu.mapapi.MKLocationManager;
import com.baidu.mapapi.MKSearch;

/*********************************************************
 * @author : zhaohua
 * @version : 2012-8-6
 * @see : 地图管理器，用于处理地图相关的业务
 * @Copyright : copyrights reserved by personal 2007-2011
 **********************************************************/
public class MyBMapManager
{
    final static String TAG = "MyBMapManager";
    // 5087F4E6BE0642A53091725FE769CD4B62FA9F1E 1FE91619DB21A03EF42A5892D69CEB33C1ED1648
    public static final String mKey = "5087F4E6BE0642A53091725FE769CD4B62FA9F1E";
    /** 有效时间差值 */
    private static final int VALID_TIME = 1000 * 60 * 2;
    /** 有效精度差值 */
    private static final int VALID_ACCACURY = 200;
    /** 最大相差距离 */
    public static final int MAX_DISTANCE = 200;
    /** 百度MapAPI的管理类 */
    private BMapManager mBMapMan = null;
    protected Context mContext;
    private Handler mHandler = new Handler();
    private CycledThread mLocTask;
    private CycledThread mLocKeepThread;
    private OnLocationListener mOnLocationListener;
    private GetLocationListener mGetLocationListener = new GetLocationListener();
    /** 当前最好一个有效的位置 */
    private Location currentBestLocation;
    /** 当前位置的地址*/
    protected MKAddrInfo mMyAddress;
    protected MKAddrInfo mOtherAddress;
    private MyAddrListener mMyAddrListener;
    private MyAddrListener mOhterAddrListener;
    private CycledThread mMyAddrTask;
    private CycledThread mOtherAddrTask;
    /** 搜索引擎*/
    private MKSearch mKMySearch;
    private MKSearch mKOtherSearch;
    private boolean mIsGetAddr;
    private int     mLocRetryCount;
        
    private static MyBMapManager instance;
    /**
     * 获取单例
     * 
     * @param context
     *            上下文
     * @return
     */
    public static MyBMapManager getInstace(Context context)
    {
        if (instance == null)
        {
            instance = new MyBMapManager(context);
        }
        return instance;
    }
    private MyBMapManager(Context context)
    {
        mContext = context;
        mBMapMan = new BMapManager(context);
        mBMapMan.init(mKey, mGeneralListener);
        mBMapMan.getLocationManager().enableProvider(MKLocationManager.MK_GPS_PROVIDER);
        mBMapMan.getLocationManager().enableProvider(MKLocationManager.MK_NETWORK_PROVIDER);
        mBMapMan.getLocationManager().setNotifyInternal(600, 10);
        
        if(Constant.mIsFixLocation)
        {
            currentBestLocation = new Location("fixed");
            currentBestLocation.setLatitude(23.005942);
            currentBestLocation.setLongitude(113.741216);
            
            mMyAddress = new MKAddrInfo();
            mMyAddress.addressComponents = new MKGeocoderAddressComponent();
            mMyAddress.addressComponents.city = "东莞市";
            mMyAddress.addressComponents.district="南城区";
            mMyAddress.addressComponents.province="广东省";
            mMyAddress.addressComponents.street="三元路邮政局大楼";
            mMyAddress.geoPt = Util.locationToGeoPoint(currentBestLocation);
            mMyAddress.strAddr = "广东省东莞市南城区三元路邮政局大楼";
            mMyAddress.strBusiness="南城区";
        }
    }
    
    /**
     * 在引用的activity的onResume中执行
     */
    public void resume()
    {
        mBMapMan.start();
    }

    /**
     * 在引用的activity的onPause中执行
     */
    public void pause()
    {
        mBMapMan.stop();
    }

    /**
     * 当不需要地图的时候引用，通常是onTerminate
     */
    public void destory()
    {
        if (mBMapMan != null)
        {
            mBMapMan.destroy();
            mBMapMan = null;
        }
    }

    public BMapManager getBMapManager()
    {
        return mBMapMan;
    }

    /** 设置位置监听器，运行在UI线程, 只对最后一个监听器有效 */
    public void setOnLocationListener(OnLocationListener listener)
    {
        this.mOnLocationListener = listener;
    }
  
    /** 开始定位 */
    public void startLocation(boolean isGetAddress)
    {
        int mLocMode = MyApplication.getContext().getLocMode();
        switch (mLocMode)
        {
            case Constant.LOC_CUR:
            {
                startLocation(isGetAddress, false);
                break;
            }
            case Constant.LOC_MAP:
            case Constant.LOC_CITY:
            {    
                mHandler.postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        if(mOnLocationListener != null)
                        {
                            Location specLoc = getMyLoation();
                            if(specLoc != null)
                            {
                                mOnLocationListener.onLocation(specLoc);
                                if (mOtherAddress != null)
                                {
                                    mOnLocationListener.onGetAddr(mOtherAddress);
                                } else
                                {
                                    mIsGetAddr = true;
                                    startAddr(specLoc);
                                }
                            }else
                            {
                                startLocation(true, false);
                            }
                        }
                    }
                }, 200);
                break;
            }
            default:
            {
                startLocation(isGetAddress, false);
                break;
            }
        }
    }
    
    /** 开始定位 */
    public void startLocation(boolean isGetAddress,boolean update)
    {
        if(Constant.mIsFixLocation)
        {
            mHandler.postDelayed(new Runnable()
            {
                @Override
                public void run()
                {
                    if(mOnLocationListener != null)
                    {
                        mOnLocationListener.onLocation(currentBestLocation);
                        if(mMyAddress != null)
                        {
                            mOnLocationListener.onGetAddr(mMyAddress);
                        }else
                        {
                            mIsGetAddr = true;
                            doGetAddr(currentBestLocation,true);
                        }
                    }
                }
            },3000);
            return;
        }
        mIsGetAddr = isGetAddress;
        // 定位检核
        if(currentBestLocation != null && !update)
        {
            Location lastLoc = mBMapMan.getLocationManager().getLocationInfo();
            if(isBetterLocation(lastLoc))
            {
                currentBestLocation = lastLoc;
                mHandler.postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        if(mOnLocationListener != null)
                        {
                            mOnLocationListener.onLocation(currentBestLocation);
                            if(mMyAddress != null)
                            {
                                mOnLocationListener.onGetAddr(mMyAddress);
                            }else
                            {
                                mIsGetAddr = true;
                                doGetAddr(currentBestLocation,true);
                            }
                        }
                    }
                },3000);
                return;
            }
        }
        if(isLocating())
        {
            // 正在定位
            return;
        }
        try{
			mBMapMan.getLocationManager().requestLocationUpdates(mGetLocationListener);
			mBMapMan.start();
			mLocTask = new CycledThread(200, 10000, new OnTimeoutListener() 
			{
				@Override
				public void onTimeout() 
				{
				    mHandler.post(new Runnable()
			        {
			            @Override
			            public void run()
			            {
        					mLocTask = null;
        					mBMapMan.getLocationManager().removeUpdates(mGetLocationListener);
        					mBMapMan.stop();
        					mBMapMan.start();
        					if(mLocRetryCount >=3 )
        					{
        					    // 定位三次失败，则认定为定位失败
            					onLocErr();
        					}else 
        					{
        					    Log.d(TAG, "-->re location count = " + mLocRetryCount);
        					    mLocRetryCount++;
        					    startLocation(true,true);
        					}
			            }
			        });
				}
			});
			mLocTask.start();
        } catch (Exception e) {
			Log.w(TAG, e);
			//重复启动容错
		}
    }
    
    /** 开始获取指定位置的地址*/
    public void startGetAddr()
    {
        int mLocMode = MyApplication.getContext().getLocMode();
        switch (mLocMode)
        {
            case Constant.LOC_CUR:
            {
                startGetMyAddr();
                break;
            }
            case Constant.LOC_CITY:
            case Constant.LOC_MAP:
            {   
                mHandler.postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        if(mOnLocationListener != null)
                        {
                            if (mOtherAddress != null)
                            {
                                mOnLocationListener.onGetAddr(mOtherAddress);
                            } else
                            {
                                mIsGetAddr = true;
                                startAddr(getMyLoation());
                            }
                        }
                    }
                }, 200);
                break;
            }
            default:
            {
                startGetMyAddr();
                break;
            }
        }
    }
    
    /** 开始获取自己位置的地址*/
    void startGetMyAddr()
    {
        if(currentBestLocation != null && mOnLocationListener != null)
        {
            if(mMyAddress != null)
            {
                mOnLocationListener.onGetAddr(mMyAddress);
            }else
            {
                doGetAddr(currentBestLocation,true);
            }
        }
    }

    /** 启动持久间歇性定位 */
    public void startKeepLocation()
    {       
        if(Constant.mIsFixLocation)
        {
            return;
        }
        if(mLocKeepThread == null)
        {
            mLocKeepThread = new CycledThread(new Runnable()
            {
                @Override
                public void run()
                {
                    Log.d(TAG, "-->startKeepLocation start ...");
                    mHandler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            startLocation(true,true);
                        }
                    }); 
                }
            }, 300000);
        }
        if(!mLocKeepThread.isAlive())
        {
            mLocKeepThread.start();
        }
    }
    
    /** 停止持久间歇性定位 */
    public void stopKeepLocation()
    {
        if(mLocKeepThread != null && mLocKeepThread.isAlive())
        {
            mLocKeepThread._stop();
            mLocKeepThread.interrupt();
            mLocKeepThread = null;
        }
    }
    
    /** 是否正在定位 */
    public boolean isLocating()
    {
        return (mLocTask != null && mLocTask.isAlive());
    }
    
    /** 获取最后有效的位置 */
    public Location getMyLoation(boolean myslef)
    {
    	if(currentBestLocation == null)
    	{
    		currentBestLocation = new Location("init");
    		currentBestLocation.setLatitude(23.034243);
    		currentBestLocation.setLongitude(113.75473);
    	}
        return currentBestLocation;
    }
    
    public Location getMyLoation()
    {
        int mLocMode = MyApplication.getContext().getLocMode();
        switch (mLocMode)
        {
            case Constant.LOC_CUR:
            {
                return getMyLoation(true);
            }
            case Constant.LOC_MAP:
            {
                Addr addr = MyApplication.getContext().getLocMapValue();
                Location loc = new Location("mocmap");
                loc.setLatitude(addr.latitude);
                loc.setLongitude(addr.longitude);
                return loc;
            }
            case Constant.LOC_CITY:
            {
                Location loc = DataCacheManager.getInstance().getLocationByAId(MyApplication.getContext().getLocCityValue());
                if(loc == null)
                {
                    loc = MyApplication.getContext().getLastLocation();
                }
                if(loc == null)
                {
                    // 用当前的
                    return getMyLoation(true);
                }else
                {
                    return loc;
                }
            }
            default:
            {
                return getMyLoation(true);
            }
        }
    }
    
    public MKAddrInfo getMyAddrInfo()
    {
        int mLocMode = MyApplication.getContext().getLocMode();
        switch(mLocMode)
        {
            case Constant.LOC_CUR:
            {
                return getMyAddrInfo(true);
            }
            case Constant.LOC_MAP:
            case Constant.LOC_CITY:
            {
                if(getMyAddrInfo(false) == null)
                {
                    return null;
                }else
                {
                    return getMyAddrInfo(false);
                }
            }
            default:
            {
                // 默认为 当前位置
                return getMyAddrInfo(true);
            }
        }
    }
    
    /** 获取最后有效的位置对应的地址信息*/
    public MKAddrInfo getMyAddrInfo(boolean myself)
    {
        return myself ? mMyAddress : mOtherAddress;
    }
    
    /** 获取地址，通过指定的位置*/
    public void startAddr(Location specLoc)
    {
        mOtherAddress = null;
        doGetAddr(specLoc, false);
    }
    
    private void onLocOK()
    {
        mLocRetryCount = 0;
        Log.d(TAG, "-->get location:" + currentBestLocation.getLongitude() + "," + currentBestLocation.getLatitude());
        if (mOnLocationListener != null)
        {
             mOnLocationListener.onLocation(currentBestLocation);
        }
    }

    private void onLocErr()
    {
        mLocRetryCount = 0;
        Log.d(TAG, "-->get location timeout ");
        if (mOnLocationListener != null)
        {
            mHandler.post(new Runnable()
            {
                @Override
                public void run()
                {
                    Location lastLoc = mBMapMan.getLocationManager().getLocationInfo();
                    if(isBetterLocation(lastLoc))
                    {
                        currentBestLocation = lastLoc;
                        mOnLocationListener.onLocation(currentBestLocation);
                        if(mMyAddress != null)
                        {
                            mOnLocationListener.onGetAddr(mMyAddress);
                        }else
                        {
                            mIsGetAddr = true;
                            doGetAddr(currentBestLocation,true);
                        }
                    }else
                    {
                        mOnLocationListener.onError();
                    }
                }
            });
        }
    }

    private void onGetAddr(final boolean myslef)
    {        
        if(myslef)
        {
            if(mMyAddrTask != null)
            {
                mMyAddrTask._stop();
                mMyAddrTask = null;
            }
        }else
        {
            if(mOtherAddrTask != null)
            {
                mOtherAddrTask._stop();
                mOtherAddrTask = null;
            }
        }
        if (mOnLocationListener != null)
        {
            mHandler.post(new Runnable()
            {
                @Override
                public void run()
                {                   
                     mOnLocationListener.onGetAddr(myslef ? mMyAddress : mOtherAddress);                    
                }
            });
        }
    }
    
    void doGetAddr(final Location specLoc,final boolean myself)
    {
        if(MyApplication.getContext().isWifi3GNetwork())
        {
            doGetAddr1(specLoc, myself);
        }else
        {
            new CycledThread(new Runnable()
            {
                @Override
                public void run()
                {
                    if(myself)
                    {
                        if(mMyAddress != null) return;
                    }else
                    {
                        if(mOtherAddress != null) return;
                    }
                    mHandler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {  
                            doGetAddr1(specLoc, myself);
                        }
                    });
                }
            }, 1500, 4500, new OnTimeoutListener()
            {
                @Override
                public void onTimeout()
                {
                    if(myself)
                    {
                        if(mMyAddress != null) return;
                        if(mMyAddrListener == null)
                        {
                            mMyAddrListener    =  new MyAddrListener(specLoc, myself);
                        }
                        mMyAddrListener.setLocation(specLoc);
                        doGetAddr2(mMyAddrListener);
                    }else
                    {
                        if(mOtherAddress != null) return;
                        if(mOhterAddrListener == null)
                        {
                            mOhterAddrListener =  new MyAddrListener(specLoc, myself);
                        }
                        mOhterAddrListener.setLocation(specLoc);
                        doGetAddr2(mOhterAddrListener);
                    }
                }
            }).start();
        }
    }
    
    void doGetAddr1(Location specLoc,boolean myself)
    {
        if (mIsGetAddr)
        {
            if(myself)
            {
                if (mKMySearch == null)
                {
                    mKMySearch = new MKSearch();
                    if(mMyAddrListener == null)
                    {
                        mMyAddrListener    =  new MyAddrListener(specLoc, myself);
                    }                 
                }
                mBMapMan.stop();
                mBMapMan.start();
                mMyAddrListener.setLocation(specLoc);
                mKMySearch.init(mBMapMan,mMyAddrListener);               
                mKMySearch.reverseGeocode(Util.locationToGeoPoint(specLoc));
                mBMapMan.start();
                Log.d(TAG, "--> doGetMyAddr ");
            }else
            {
                if (mKOtherSearch == null)
                {
                    mKOtherSearch = new MKSearch();
                    if(mOhterAddrListener == null)
                    {
                        mOhterAddrListener =  new MyAddrListener(specLoc, myself);
                    }                   
                }
                mBMapMan.stop();
                mBMapMan.start();
                mOhterAddrListener.setLocation(specLoc);
                mKOtherSearch.init(mBMapMan, mOhterAddrListener);
                mKOtherSearch.reverseGeocode(Util.locationToGeoPoint(specLoc));
                mBMapMan.start();
                Log.d(TAG, "--> doGetOtherAddr ");
            }
        }
    }
    
    void doGetAddr2(MyAddrListener addrListener)
    {
        if(addrListener.getMyself())
        {
            if(mMyAddrTask == null)
            {
                mMyAddrTask = new CycledThread(addrListener , 5000, addrListener);
                mMyAddrTask.start();
            }
        }else
        {
            if(mOtherAddrTask == null)
            {
                mOtherAddrTask = new CycledThread(addrListener , 5000, addrListener);
                mOtherAddrTask.start();
            }
        }
    }
    
    /**
     * Determines whether one Location reading is better than the current
     * Location fix
     * 
     * @param location
     *            The new Location that you want to evaluate
     * @param currentBestLocation
     *            The current Location fix, to which you want to compare the new
     *            one
     */
    protected boolean isBetterLocation(Location location)
    {
        if (location == null) { return false; }
        if (currentBestLocation == null || currentBestLocation.getProvider().equals("init"))
        {
            // A new location is always better than no location
            return true;
        }
        // check whether distance valid range
        if (location.distanceTo(currentBestLocation) < MAX_DISTANCE) { return true; }
        // Check whether the new location fix is newer or older
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isSignificantlyNewer = timeDelta > VALID_TIME;
        boolean isSignificantlyOlder = timeDelta < -VALID_TIME;
        boolean isNewer = timeDelta > 0;
        // If it's been more than two minutes since the current location, use
        // the new location
        // because the user has likely moved
        if (isSignificantlyNewer)
        {
            return true;
        } else
        // If the new location is more than two minutes older, it must be worse
        if (isSignificantlyOlder) { return false; }
        // Check whether the new location fix is more or less accurate
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
                .getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > VALID_ACCACURY;
        // Check if the old and new location are from the same provider
        boolean isFromSameProvider = isSameProvider(location.getProvider(),
                currentBestLocation.getProvider());
        // Determine location quality using a combination of timeliness and
        // accuracy
        if (isMoreAccurate)
        {
            return true;
        } else if (isNewer && !isLessAccurate)
        {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate
                && isFromSameProvider) { return true; }
        return false;
    }

    /** Checks whether two providers are the same */
    private boolean isSameProvider(String provider1, String provider2)
    {
        if (provider1 == null) { return provider2 == null; }
        return provider1.equals(provider2);
    }

    /** 位置监听器 */
    public static interface OnLocationListener
    {
        public void onError();

        public void onLocation(Location loc);
        
        public void onGetAddr(MKAddrInfo addr);
    }

    /** 获取位置的监听器 */
    private class GetLocationListener implements LocationListener
    {
        @Override
        public void onLocationChanged(Location loc)
        {
            if (isBetterLocation(loc))
            {
                if (mLocTask != null)
                {
                    mLocTask._stop();
                    mLocTask = null;
                }
                mBMapMan.getLocationManager().removeUpdates(mGetLocationListener);
                mBMapMan.stop();
                mBMapMan.start();
                // 更新我的位置状态
                boolean addrChanged = true;
                if(currentBestLocation != null && mMyAddress != null && currentBestLocation.distanceTo(loc) < 20)
                {
                    addrChanged = false;
                }
                currentBestLocation = loc;
                onLocOK();
                if(addrChanged)
                {
                    doGetAddr(currentBestLocation,true);
                }else
                {
                    onGetAddr(true);
                }
            }
        }
    }

    private MKGeneralListener mGeneralListener = new MKGeneralListener()
    {
        @Override
        public void onGetNetworkState(int arg0)
        {

        }

        @Override
        public void onGetPermissionState(int arg0)
        {

        }
    };

    private class MyAddrListener extends BaseSearchListenerImpl implements Runnable, OnTimeoutListener
    {
        private boolean isMyself;
        private Location specLocation;
        
        public MyAddrListener(Location loc, boolean myself)
        {
            this.isMyself = myself;
            this.specLocation = loc;
        }
        
        public void setLocation(Location loc)
        {
            this.specLocation = loc;
        }
        
        public boolean getMyself()
        {
            return isMyself;
        }
        
        @Override
        public void onGetAddrResult(MKAddrInfo paramMKAddrInfo,int paramInt)
        {
            if (paramInt != 0)
            {
                if (isMyself)
                {
                    mMyAddress = null;
                } else
                {
                    mOtherAddress = null;
                }
            } else
            {
                if (isMyself)
                {
                    mMyAddress = paramMKAddrInfo;
                    Log.d(TAG,
                            "-->get mMyAddress Addr :"
                                    + (mMyAddress != null ? mMyAddress.strAddr: null));
                } else
                {
                    mOtherAddress = paramMKAddrInfo;
                    Log.d(TAG,
                            "-->get mOtherAddress Addr :"
                                    + (mOtherAddress != null ? mOtherAddress.strAddr: null));
                }
            }
            onGetAddr(isMyself);
        }
        
        @Override
        public void run()
        {                    
            for (int i = 0; i < 3; i++)
            {
                try
                {
                    if (isMyself)
                    {
                        if(mMyAddress != null)
                        {
                            return;
                        }
                    }else
                    {
                        if(mOtherAddress != null)
                        {
                            return;
                        }
                    }
//                    Address addr = GoogleAPIManger.reverseGeocode(
//                            specLocation.getLatitude(),
//                            specLocation.getLongitude());
                    Address addr = BDAPIManager.reverseGeocode(
                          specLocation.getLatitude(),
                          specLocation.getLongitude(),
                          mKey);
                    if(addr == null)
                    {
                        continue;
                    }
                    // 获取成功
                    MKAddrInfo paramMKAddrInfo = new MKAddrInfo();
                    paramMKAddrInfo.geoPt = Util.locationToGeoPoint(specLocation);
                    paramMKAddrInfo.strAddr = addr.getAddress();
                    paramMKAddrInfo.addressComponents = new MKGeocoderAddressComponent();
                    paramMKAddrInfo.addressComponents.city = addr.city;
                    paramMKAddrInfo.addressComponents.district = addr.county;
                    paramMKAddrInfo.addressComponents.province = addr.province;
                    paramMKAddrInfo.addressComponents.street = addr.line;
                    if (isMyself)
                    {
                        if(mMyAddress == null)
                        {
                            mMyAddress = paramMKAddrInfo;
                            Log.d(TAG,
                                "-->get2 mMyAddress Addr :"
                                        + (mMyAddress != null ? mMyAddress.strAddr: null));
                        }else
                        {
                            return;
                        }
                    } else
                    {
                        if(mOtherAddress == null)
                        {
                            mOtherAddress = paramMKAddrInfo;
                            Log.d(TAG,
                                    "-->get2 mOtherAddress Addr :"
                                            + (mOtherAddress != null ? mOtherAddress.strAddr: null));
                        }else
                        {
                            return;
                        }
                    }
                    onGetAddr(isMyself);
                    return;
                } catch (ClientProtocolException e)
                {
                    e.printStackTrace();
                } catch (JSONException e)
                {
                    e.printStackTrace();
                } catch (IOException e)
                {
                    e.printStackTrace();
                }
                // 获取失败
                Log.d(TAG, "--> doGetAddr2 no result retry = "+ i);
            }
            if (isMyself)
            {
                mMyAddress = null;
            } else
            {
                mOtherAddress = null;
            }
            onGetAddr(isMyself);
        }
        
        @Override
        public void onTimeout()
        {
            if(isMyself)
            {
                mMyAddress = null;
            }else
            {
                mOtherAddress = null;
            }
            onGetAddr(isMyself);
        }
    }
}